#include <utils/Profiler.h>
#include <iostream>
#include <fstream>
#include <iomanip>
#include <sstream>

namespace myamg {
    Profiler::ScopedTimer::ScopedTimer(Profiler &profiler, const std::string &name, const std::string &location)
        : profiler_(profiler), name_(name) {
        profiler_.start(name_, location);
    }

    Profiler::ScopedTimer::~ScopedTimer() {
        profiler_.end(name_, "");
    }

    Profiler &Profiler::instance() {
        static Profiler inst;
        return inst;
    }

    void Profiler::start(const std::string &name, const std::string &location) {
        TimeRecord record;
        record.name = name;
        record.startLocation = location;
        record.startTime = std::chrono::high_resolution_clock::now();
        record.completed = false;

        activeTimers_[name] = record;
    }

    void Profiler::end(const std::string &name, const std::string &location) {
        auto end_time = std::chrono::high_resolution_clock::now();

        auto it = activeTimers_.find(name);
        if (it != activeTimers_.end()) {
            it->second.endTime = end_time;
            it->second.endLocation = location;
            it->second.durationMs = std::chrono::duration<double, std::milli>(
                end_time - it->second.startTime).count();
            it->second.completed = true;

            records_.push_back(it->second);
            activeTimers_.erase(it);
        }
    }

    void Profiler::reset() {
        records_.clear();
        activeTimers_.clear();
    }

    void Profiler::printToConsole(bool detailed) const {
        std::cout << "\n========== Profile Report ==========\n";
        std::cout << std::fixed << std::setprecision(3);

        if (detailed && !records_.empty()) {
            std::cout << "\nDetailed Records:\n";
            std::cout << std::string(80, '-') << "\n";
            for (size_t i = 0; i < records_.size(); ++i) {
                const auto &record = records_[i];
                std::cout << "[" << (i + 1) << "] " << record.name << "\n";
                std::cout << "    Duration: " << record.durationMs << " ms\n";
                if (!record.startLocation.empty()) {
                    std::cout << "    Location: " << record.startLocation << "\n";
                }
                if (!record.endLocation.empty()) {
                    std::cout << "    End: " << record.endLocation << "\n";
                }
                if (i < records_.size() - 1) {
                    std::cout << "\n";
                }
            }
            std::cout << std::string(80, '-') << "\n";
        }

        std::map<std::string, std::vector<double> > summary;
        for (const auto &record: records_) {
            summary[record.name].push_back(record.durationMs);
        }

        if (!summary.empty()) {
            size_t max_name_len = 4;
            for (const auto &pair: summary) {
                if (pair.first.length() > max_name_len) {
                    max_name_len = pair.first.length();
                }
            }
            max_name_len = std::min(max_name_len, size_t(50));

            std::cout << "\nSummary Statistics:\n";
            size_t total_width = max_name_len + 8 + 15 + 15 + 15 + 15 + 6;
            std::cout << std::string(total_width, '-') << "\n";

            std::cout << std::left << std::setw(max_name_len) << "Name" << " | "
                    << std::right << std::setw(6) << "Count" << " | "
                    << std::setw(13) << "Total (ms)" << " | "
                    << std::setw(13) << "Avg (ms)" << " | "
                    << std::setw(13) << "Min (ms)" << " | "
                    << std::setw(13) << "Max (ms)" << "\n";
            std::cout << std::string(total_width, '-') << "\n";

            for (const auto &pair: summary) {
                const auto &times = pair.second;
                double total = 0;
                double min_time = times[0];
                double max_time = times[0];

                for (double t: times) {
                    total += t;
                    if (t < min_time) min_time = t;
                    if (t > max_time) max_time = t;
                }

                double average = total / times.size();

                std::string name = pair.first;
                if (name.length() > max_name_len) {
                    name = name.substr(0, max_name_len - 3) + "...";
                }

                std::cout << std::left << std::setw(max_name_len) << name << " | "
                        << std::right << std::setw(6) << times.size() << " | "
                        << std::setw(13) << total << " | "
                        << std::setw(13) << average << " | "
                        << std::setw(13) << min_time << " | "
                        << std::setw(13) << max_time << "\n";
            }
            std::cout << std::string(total_width, '-') << "\n";
        }
        std::cout << "\n";
    }

    void Profiler::saveToFile(const std::string &filename, bool detailed) const {
        std::ofstream file(filename);
        if (!file.is_open()) {
            std::cerr << "Failed to open file: " << filename << "\n";
            return;
        }

        file << "\n========== Profile Report ==========\n";
        file << std::fixed << std::setprecision(3);

        if (detailed && !records_.empty()) {
            file << "\nDetailed Records:\n";
            file << std::string(80, '-') << "\n";
            for (size_t i = 0; i < records_.size(); ++i) {
                const auto &record = records_[i];
                file << "[" << (i + 1) << "] " << record.name << "\n";
                file << "    Duration: " << record.durationMs << " ms\n";
                if (!record.startLocation.empty()) {
                    file << "    Location: " << record.startLocation << "\n";
                }
                if (!record.endLocation.empty()) {
                    file << "    End: " << record.endLocation << "\n";
                }
                if (i < records_.size() - 1) {
                    file << "\n";
                }
            }
            file << std::string(80, '-') << "\n";
        }

        std::map<std::string, std::vector<double> > summary;
        for (const auto &record: records_) {
            summary[record.name].push_back(record.durationMs);
        }

        if (!summary.empty()) {
            size_t max_name_len = 4;
            for (const auto &pair: summary) {
                if (pair.first.length() > max_name_len) {
                    max_name_len = pair.first.length();
                }
            }
            max_name_len = std::min(max_name_len, size_t(50));

            file << "\nSummary Statistics:\n";
            size_t total_width = max_name_len + 8 + 15 + 15 + 15 + 15 + 6;
            file << std::string(total_width, '-') << "\n";

            file << std::left << std::setw(max_name_len) << "Name" << " | "
                    << std::right << std::setw(6) << "Count" << " | "
                    << std::setw(13) << "Total (ms)" << " | "
                    << std::setw(13) << "Avg (ms)" << " | "
                    << std::setw(13) << "Min (ms)" << " | "
                    << std::setw(13) << "Max (ms)" << "\n";
            file << std::string(total_width, '-') << "\n";

            for (const auto &pair: summary) {
                const auto &times = pair.second;
                double total = 0;
                double min_time = times[0];
                double max_time = times[0];

                for (double t: times) {
                    total += t;
                    if (t < min_time) min_time = t;
                    if (t > max_time) max_time = t;
                }

                double average = total / times.size();

                std::string name = pair.first;
                if (name.length() > max_name_len) {
                    name = name.substr(0, max_name_len - 3) + "...";
                }

                file << std::left << std::setw(max_name_len) << name << " | "
                        << std::right << std::setw(6) << times.size() << " | "
                        << std::setw(13) << total << " | "
                        << std::setw(13) << average << " | "
                        << std::setw(13) << min_time << " | "
                        << std::setw(13) << max_time << "\n";
            }
            file << std::string(total_width, '-') << "\n";
        }
        file << "\n";

        file.close();
    }

    std::string Profiler::getFormattedReport(bool detailed) const {
        std::ostringstream oss;

        oss << "\n========== Profile Report ==========\n";
        oss << std::fixed << std::setprecision(3);

        if (detailed && !records_.empty()) {
            oss << "\nDetailed Records:\n";
            oss << std::string(80, '-') << "\n";
            for (size_t i = 0; i < records_.size(); ++i) {
                const auto &record = records_[i];
                oss << "[" << (i + 1) << "] " << record.name << "\n";
                oss << "    Duration: " << record.durationMs << " ms\n";
                if (!record.startLocation.empty()) {
                    oss << "    Location: " << record.startLocation << "\n";
                }
                if (!record.endLocation.empty()) {
                    oss << "    End: " << record.endLocation << "\n";
                }
                if (i < records_.size() - 1) {
                    oss << "\n";
                }
            }
            oss << std::string(80, '-') << "\n";
        }

        std::map<std::string, std::vector<double> > summary;
        for (const auto &record: records_) {
            summary[record.name].push_back(record.durationMs);
        }

        if (!summary.empty()) {
            size_t max_name_len = 4;
            for (const auto &pair: summary) {
                if (pair.first.length() > max_name_len) {
                    max_name_len = pair.first.length();
                }
            }
            max_name_len = std::min(max_name_len, size_t(50));

            oss << "\nSummary Statistics:\n";
            size_t total_width = max_name_len + 8 + 15 + 15 + 15 + 15 + 6;
            oss << std::string(total_width, '-') << "\n";

            oss << std::left << std::setw(max_name_len) << "Name" << " | "
                    << std::right << std::setw(6) << "Count" << " | "
                    << std::setw(13) << "Total (ms)" << " | "
                    << std::setw(13) << "Avg (ms)" << " | "
                    << std::setw(13) << "Min (ms)" << " | "
                    << std::setw(13) << "Max (ms)" << "\n";
            oss << std::string(total_width, '-') << "\n";

            for (const auto &pair: summary) {
                const auto &times = pair.second;
                double total = 0;
                double min_time = times[0];
                double max_time = times[0];

                for (double t: times) {
                    total += t;
                    if (t < min_time) min_time = t;
                    if (t > max_time) max_time = t;
                }

                double average = total / times.size();

                std::string name = pair.first;
                if (name.length() > max_name_len) {
                    name = name.substr(0, max_name_len - 3) + "...";
                }

                oss << std::left << std::setw(max_name_len) << name << " | "
                        << std::right << std::setw(6) << times.size() << " | "
                        << std::setw(13) << total << " | "
                        << std::setw(13) << average << " | "
                        << std::setw(13) << min_time << " | "
                        << std::setw(13) << max_time << "\n";
            }
            oss << std::string(total_width, '-') << "\n";
        }
        oss << "\n";

        return oss.str();
    }

    const std::vector<Profiler::TimeRecord> &Profiler::getRecords() const {
        return records_;
    }
}
